import json
import os
from typing import Type, Generic, TypeVar, List, get_args, get_origin, Any, Dict
from pydantic import BaseModel
from pydantic.generics import GenericModel
from settings import settings


K = TypeVar('K')
T = TypeVar('T')


class _ListMode(GenericModel, Generic[T]):
    values: List[T]


class _DictMode(GenericModel, Generic[K, T]):
    values: Dict[K, T]


def _save(filename: str, config: BaseModel | dict | list | str | int | float | bool):
    os.makedirs(os.path.dirname(filename), exist_ok=True)
    with open(filename, 'wt+') as file:
        if type(config) in [str, int, float, bool]:
            file.write(str(config))
        elif type(config) == list:
            file.write(_ListMode[Any](values=config).json(indent=4))
        elif type(config) == dict:
            file.write(_DictMode[Any, Any](values=config).json(indent=4))
        else:
            file.write(config.json(indent=4))


def _load(filename: str, model: Type, default):
    try:
        with open(filename, 'rt') as file:
            if model in [str, int, float]:
                return model(file.read().rstrip('\n\r\t '))
            elif model == bool:
                return file.read().strip() == 'True'
            elif model == list:
                return _ListMode[Any](**json.load(file)).values
            elif get_origin(model) == list:
                return _ListMode[get_args(model)[0]](**json.load(file)).values
            elif model == dict:
                return _DictMode[Any, Any](**json.load(file)).values
            elif get_origin(model) == dict:
                args = get_args(model)
                return _DictMode[args[0], args[1]](**json.load(file)).values
            else:
                return model(**json.load(file))
    except Exception as e:
        return default


def save_config(filename: str, config: BaseModel | dict | list | str | int | float | bool):
    filename = f'{settings.ROOT_PATH}/environment/configs/{filename}.save'
    _save(filename, config)


def load_config(filename: str, model: Type, default):
    filename = f'{settings.ROOT_PATH}/environment/configs/{filename}.save'
    return _load(filename, model, default)


def save_const(filename: str, config: BaseModel | dict | list | str | int | float | bool):
    filename = f'/jffs2/{filename}.const'
    _save(filename, config)


def load_const(filename: str, model: Type, default=None):
    filename = f'/jffs2/{filename}.const'
    return _load(filename, model, default)
